#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <conio.h>
#include <time.h>

// 定义方向键的键值
#define UP 72
#define DOWN 80
#define LEFT 75
#define RIGHT 77
#define ESC 27
#define F1 59
#define F2 60

// 定义地图大小
#define HEIGHT 20
#define WIDTH 40

// 定义蛇的结构体
typedef struct {
    int x;
    int y;
} Position;

typedef struct {
    Position pos[800];    // 蛇身坐标数组
    int length;          // 蛇的长度
    int direction;       // 蛇的方向
    int speed;          // 蛇的速度
    int score;          // 当前得分
} Snake;

// 定义食物结构体
typedef struct {
    int x;
    int y;
} Food;

// 函数声明
void gotoxy(int x, int y);                // 光标定位
void hideCursor();                        // 隐藏光标
void setColor(int color);                 // 设置颜色
void printSnake();                        // 打印蛇的字符画
void welcomeToGame();                     // 欢迎界面
void createMap();                         // 创建地图
void scoreAndTips();                      // 显示得分和提示
void fileOut();                           // 读取最高分
void initSnake();                         // 初始化蛇
void createFood();                        // 创建食物
int biteSelf();                          // 判断是否咬到自己
void cantCrossWall();                    // 判断是否撞墙
void speedUp();                          // 加速
void speedDown();                        // 减速
void snakeMove();                        // 蛇的移动
void keyboardControl();                  // 键盘控制
void gameOver();                         // 游戏结束
void explanation();                      // 游戏说明
// 全局变量
Snake snake;
Food food;
int highestScore = 0;
int sleepTime = 330;    // 控制蛇的速度

// 光标定位函数
void gotoxy(int x, int y) {
    COORD coord;
    coord.X = x;
    coord.Y = y;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}

// 隐藏光标
void hideCursor() {
    CONSOLE_CURSOR_INFO cursor_info = {1, 0};//光标大小为1，是否显示光标为0，不显示
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}

// 设置文字颜色
void setColor(int color) {
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
}

// 打印蛇的字符画
void printSnake() {
    system("cls");    // 清屏
    setColor(14);     // 设置黄色
    printf("\n\n\n");
    printf("   _________         _________\n");
    printf("  /         \\       /         \\\n");
    printf(" /  /~~~~~\\  \\     /  /~~~~~\\  \\\n");
    printf(" |  |     |  |     |  |     |  |\n");
    printf(" |  |     |  |     |  |     |  |\n");
    printf(" |  |     |  |     |  |     |  |         /\n");
    printf(" |  |     |  |     |  |     |  |       //\n");
    printf("(o  o)    \\  \\_____/  /     \\  \\_____/ /\n");
    printf(" \\__/      \\         /       \\        /\n");
    printf("  |         ~~~~~~~~~         ~~~~~~~~\n");
    printf("  ^\n");
    printf("        《贪吃蛇大作战》\n\n");
}

// 欢迎界面
void welcomeToGame() {
    printSnake();
    printf("\n\n");
    printf("            1. 开始游戏\n");
    printf("            2. 游戏说明\n");
    printf("            3. 退出游戏\n\n");
    printf("            请选择[1-3]: ");
    
    char choice;
    while(1) {
        choice = getch();
        switch(choice) {
            case '1':
                system("cls");
                createMap();
                initSnake();
                createFood();
                keyboardControl();
                break;
            case '2':
                explanation();
                break;
            case '3':
                exit(0);
            default:
                continue;
        }
        break;
    }
}

// 创建地图
void createMap() {
    int i, j;
    for(i = 0; i <= HEIGHT; i++) {
        gotoxy(0, i);
        printf("■");
        gotoxy(WIDTH * 2, i);
        printf("■");
    }
    for(j = 0; j <= WIDTH * 2; j += 2) {
        gotoxy(j, 0);
        printf("■");
        gotoxy(j, HEIGHT);
        printf("■");
    }
}

// 初始化蛇
void initSnake() {
    snake.length = 5;
    snake.speed = 200;
    snake.score = 0;
    snake.direction = RIGHT;
    
    // 初始化蛇身，从尾部到头部
    for(int i = 0; i < snake.length; i++) {
        snake.pos[i].x = 24 - i * 2;
        snake.pos[i].y = 5;
    }
    
    // 绘制蛇身
    for(int i = 0; i < snake.length; i++) {
        gotoxy(snake.pos[i].x, snake.pos[i].y);
        printf("★");
    }
}

// 创建食物
void createFood() {
    int flag;
    do {
        flag = 0;
        srand(time(NULL));
        // 修改范围，避免在边界上生成食物
        food.x = (rand() % (WIDTH - 2) + 1) * 2;  // 确保x坐标在1到WIDTH-1之间
        food.y = rand() % (HEIGHT - 1) + 1;       // 确保y坐标在1到HEIGHT-1之间
        
        // 检查食物是否与蛇身重叠
        for(int i = 0; i < snake.length; i++) {
            if(food.x == snake.pos[i].x && food.y == snake.pos[i].y) {
                flag = 1;
                break;
            }
        }
    } while(flag);
    
    gotoxy(food.x, food.y);
    printf("●");
}

// 判断是否咬到自己
int biteSelf() {
    for(int i = 1; i < snake.length; i++) {
        if(snake.pos[0].x == snake.pos[i].x && snake.pos[0].y == snake.pos[i].y) {
            return 1;
        }
    }
    return 0;
}

// 判断是否撞墙
void cantCrossWall() {
    if(snake.pos[0].x == 0 || snake.pos[0].x == WIDTH * 2 || 
       snake.pos[0].y == 0 || snake.pos[0].y == HEIGHT) {
        gameOver();
    }
}

// 加速
void speedUp() {
    if(sleepTime >= 50) {
        sleepTime -= 10;  // 减少10而不是直接设为10
    }
}

// 减速
void speedDown() {
    if(sleepTime <= 350) {
        sleepTime += 30;
    }
}

// 从文件读取最高分
void fileOut() {
    FILE *fp = fopen("save.txt", "r");
    if(fp != NULL) {
        fscanf(fp, "%d", &highestScore);
        fclose(fp);
    }
}

// 保存最高分到文件
void saveScore() {
    if(snake.score > highestScore) {
        FILE *fp = fopen("save.txt", "w");
        if(fp != NULL) {
            fprintf(fp, "%d", snake.score);
            fclose(fp);
        }
    }
}

// 显示得分和提示
void scoreAndTips() {
    gotoxy(WIDTH * 2 + 4, 1);
    printf("当前得分: %d", snake.score);
    gotoxy(WIDTH * 2 + 4, 2);
    printf("历史最高: %d", highestScore);
    gotoxy(WIDTH * 2 + 4, 4);
    printf("操作说明:");
    gotoxy(WIDTH * 2 + 4, 5);
    printf("↑ ↓ ← → 控制方向");
    gotoxy(WIDTH * 2 + 4, 6);
    printf("F1 加速");
    gotoxy(WIDTH * 2 + 4, 7);
    printf("F2 减速");
    gotoxy(WIDTH * 2 + 4, 8);
    printf("ESC 退出游戏");
}

// 蛇的移动
void snakeMove() {
    // 记录蛇尾
    Position tail;
    tail.x = snake.pos[snake.length - 1].x;
    tail.y = snake.pos[snake.length - 1].y;

    // 擦除蛇尾
    gotoxy(tail.x, tail.y);
    printf("  ");

    // 身体移动（除了最后一节）
    for(int i = snake.length - 1; i > 0; i--) {
        snake.pos[i].x = snake.pos[i - 1].x;
        snake.pos[i].y = snake.pos[i - 1].y;
    }

    // 移动蛇头
    switch(snake.direction) {
        case UP:
            snake.pos[0].y--;
            break;
        case DOWN:
            snake.pos[0].y++;
            break;
        case LEFT:
            snake.pos[0].x -= 2;
            break;
        case RIGHT:
            snake.pos[0].x += 2;
            break;
    }

    // 判断是否吃到食物
    if(snake.pos[0].x == food.x && snake.pos[0].y == food.y) {
        gotoxy(food.x, food.y);
        printf("  ");  // 擦除食物
        
        // 增加长度，新增的一节位置和原来的尾节位置相同
        snake.pos[snake.length].x = tail.x;
        snake.pos[snake.length].y = tail.y;
        snake.length++;

        // 加速和计分
        if(sleepTime >= 50) {
            sleepTime -= 10;
        }
        if(sleepTime < 320) {
            snake.score += 2;
        } else if(sleepTime > 350) {
            snake.score += 1;
        } else {
            snake.score += 1;
        }
        if(snake.score > highestScore) {
            highestScore = snake.score;
        }
        createFood();
    }

    // 绘制整个蛇身
    for(int i = 0; i < snake.length; i++) {
        gotoxy(snake.pos[i].x, snake.pos[i].y);
        printf("★");
    }

    // 更新得分显示
    scoreAndTips();
}

// 键盘控制
void keyboardControl() {
    while(1) {
        // 检测按键
        if(_kbhit()) {
            int key = _getch();
            if(key == 224) {
                key = _getch();
                switch(key) {
                    case UP:
                        if(snake.direction != DOWN)
                            snake.direction = UP;
                        break;
                    case DOWN:
                        if(snake.direction != UP)
                            snake.direction = DOWN;
                        break;
                    case LEFT:
                        if(snake.direction != RIGHT)
                            snake.direction = LEFT;
                        break;
                    case RIGHT:
                        if(snake.direction != LEFT)
                            snake.direction = RIGHT;
                        break;
                }
            }
            // 功能键检测
            if(key == 0) {
                key = _getch();
                switch(key) {
                    case F1:
                        speedUp();
                        break;
                    case F2:
                        speedDown();
                        break;
                }
            }
            // ESC键检测
            if(key == ESC) {
                gameOver();
            }
        }
        
        // 移动蛇
        snakeMove();
        
        // 检测是否撞墙或咬到自己
        cantCrossWall();
        if(biteSelf()) {
            gameOver();
        }
        
        Sleep(sleepTime);  // 控制蛇的速度
    }
}

// 游戏结束
void gameOver() {
    system("cls");
    saveScore();
    gotoxy(WIDTH, HEIGHT / 2);
    printf("游戏结束！\n");
    gotoxy(WIDTH, HEIGHT / 2 + 1);
    printf("您的得分：%d\n", snake.score);
    gotoxy(WIDTH, HEIGHT / 2 + 2);
    if(snake.score >= highestScore) {
        printf("创纪录啦！最高分被你刷新啦，真棒！！！\n");
    } else {
        printf("继续努力吧~你离最高分还差: %d\n", highestScore - snake.score);
    }
    gotoxy(WIDTH, HEIGHT / 2 + 3);
    printf("1. 重新开始\n");
    gotoxy(WIDTH, HEIGHT / 2 + 4);
    printf("2. 退出游戏\n");
    
    while(1) {
        char choice = getch();
        switch(choice) {
            case '1':
                system("cls");
                    // 重置所有游戏变量
                sleepTime = 330;    // 重置速度
                snake.score = 0;    // 重置分数
                createMap();        // 重新创建地图
                initSnake();        // 重新初始化蛇
                createFood();       // 重新创建食物
                keyboardControl();  // 重新开始游戏控制
                break;            // 返回主函数
            case '2':
                exit(0);
        }
    }
}

// 游戏说明
void explanation() {
    system("cls");
    printf("\n\n");
    printf("                游戏说明\n");
    printf("    --------------------------------\n");
    printf("    1. 使用方向键控制蛇的移动方向\n");
    printf("    2. F1键加速，F2键减速\n");
    printf("    3. 吃到食物得分增加，蛇身变长\n");
    printf("    4. 撞墙或咬到自己游戏结束\n");
    printf("    5. ESC键退出游戏\n");
    printf("\n    按任意键返回主菜单...");
    getch();
    system("cls");
    welcomeToGame();
}

// 主函数
int main() {
    system("chcp 65001");    // 设置控制台代码页为 UTF-8
    SetConsoleOutputCP(65001);    // 设置输出代码页为 UTF-8
    SetConsoleTitleW(L"贪吃蛇");  // 使用宽字符设置窗口标题
    system("mode con cols=100 lines=30");    // 设置窗口大小
    hideCursor();    // 隐藏光标
    
    welcomeToGame();
    return 0;
}